﻿using Autofac;
using Autofac.Extensions.DependencyInjection;
using DotNetCore.CAP.Dashboard.NodeDiscovery;
using HealthChecks.UI.Client;
using LogicApi.EntityFrameworkCore;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.OpenApi.Models;
using Open.Core.Helpers.SqlSugar;
using Open.Core.Runtime;
using Open.Repository.EntityFramework;
using Product.API.Grpc;
using Savorboard.CAP.InMemoryMessageQueue;
using StackExchange.Redis;
using System.IdentityModel.Tokens.Jwt;
using Web.Product.API.Infrastructure.Filters;
using Web.Product.API.Infrastructure.Middlewares;

namespace Web.Product.API
{

    public class Startup
    {

        public IConfiguration Configuration { get; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public virtual IServiceProvider ConfigureServices(IServiceCollection services)
        {
            #region DbConetext
            var connectionString = Configuration["ConnectionString"];
            services.AddDbContext<LogicApiDbContext>(option =>
            {
                option.UseMySql(connectionString, new MySqlServerVersion(new Version(5, 7, 28)), mySqlOptions => mySqlOptions.CommandTimeout(300));
            }).AddUnitOfWork<LogicApiDbContext>(); ;

            #endregion

            #region MVC
            services.AddMvc();
            services.AddControllers(builder =>
            {
                //全局异常过滤
                builder.Filters.Add(typeof(HttpGlobalExceptionFilter));
                //模型验证过滤
                builder.Filters.Add(typeof(ValidateModelStateFilter));

            }).AddJsonOptions(options => options.JsonSerializerOptions.WriteIndented = true).AddNewtonsoftJson();
            #endregion

            #region GRPC
            services.AddGrpc(options => { options.EnableDetailedErrors = true; });
            #endregion

            #region Ids4
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("sub");

            var identityUrl = Configuration["IdentityUrl"];

            services.AddAuthentication(options =>
            {

                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;

            }).AddJwtBearer(options =>
            {
                options.Authority = identityUrl;
                options.RequireHttpsMetadata = false;
                options.Audience = "product";
            });



            //配置session的有效时间,单位秒
            services.AddSession(options =>
            {
                options.IdleTimeout = TimeSpan.FromSeconds(30);
            });
            #endregion

            #region Swagger
            services.AddSwaggerGen(option =>
            {
                option.SwaggerDoc("v1", new OpenApiInfo
                {
                    Title = "OpenApi - Product HTTP API",
                    Version = "v1",
                    Description = "The Product Service HTTP API"
                });
                option.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
                {
                    Type = SecuritySchemeType.OAuth2,
                    Flows = new OpenApiOAuthFlows()
                    {
                        Implicit = new OpenApiOAuthFlow()
                        {
                            AuthorizationUrl = new Uri($"{identityUrl}/connect/authorize"),
                            TokenUrl = new Uri($"{identityUrl}/connect/token"),
                            RefreshUrl = new Uri($"{identityUrl}/connect/revocation"),
                            Scopes = new Dictionary<string, string>()
                             {
                                { "product", "Product Service" }
                             }
                        }
                    }
                });
                //异常过滤输出
                option.OperationFilter<AuthorizeCheckOperationFilter>();
                //接口备注信息
                option.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Product.API.xml"));
                option.EnableAnnotations();
            });
            #endregion

            #region Sdk 
            services.AddSqlSugar((IConfigurationRoot)Configuration);
            #endregion

            #region Redis
            services.AddSingleton<ConnectionMultiplexer>(option =>
            {
                string redisContion = Configuration["RedisConnection"];
                var configurations = ConfigurationOptions.Parse(redisContion, true);
                configurations.ResolveDns = true;
                return ConnectionMultiplexer.Connect(configurations);
            });
            #endregion

            #region EventBus DTC+2PC CAP
            if (Configuration.GetValue<bool>("IsServiceEventBus"))
            {
                services.AddCap(options =>
                {
                    //options.UseInMemoryStorage();
                    //options.UseInMemoryMessageQueue();

                    options.UseMySql(x =>
                    {
                        x.TableNamePrefix = "Cap";
                        x.ConnectionString = Configuration["CAPConnectionString"];
                    });
                    options.UseRabbitMQ(x =>
                    {
                        x.HostName = Configuration["RabbitMQ:HostName"];
                        x.UserName = Configuration["RabbitMQ:UserName"];
                        x.Password = Configuration["RabbitMQ:Password"];
                        x.Port = Convert.ToInt32(Configuration["RabbitMQ:Port"]);
                    });
                    options.FailedRetryCount = 3;
                    options.UseDashboard();
                    //节点发现基于consul
                    options.UseDiscovery(_ =>
                    {
                        _.DiscoveryServerHostName = "localhost";
                        _.DiscoveryServerPort = 8500;
                        _.CurrentNodeHostName = Configuration.GetValue<string>("ASPNETCORE_HOSTNAME");
                        _.CurrentNodePort = Configuration.GetValue<int>("ASPNETCORE_PORT");
                        _.NodeId = Configuration.GetValue<string>("NodeId");
                        _.NodeName = Configuration.GetValue<string>("NodeName");
                    });
                });

            }

            #endregion

            #region Health Check
            var healthBuilder = services.AddHealthChecks().AddCheck("self", () => HealthCheckResult.Healthy());
            healthBuilder.AddRedis(Configuration["RedisConnection"], name: "redis-check", tags: new string[] { "redis" });
            if (Configuration.GetValue<bool>("IsServiceEventBus"))
                healthBuilder.AddRabbitMQ($"amqp://{Configuration["EventBusConnection"]}", name: "basket-rabbitmqbus-check", tags: new string[] { "rabbitmqbus" });
            #endregion

            #region Cors
            services.AddCors(options =>
            {
                options.AddPolicy("CorsPolicy",
                    builder => builder
                    .SetIsOriginAllowed((host) => true)
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
            });
            #endregion

            #region AutoFac
            new DependencyRegistrator(services, new TypeFinder()).RegisterServices();//自动注册服务
            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            services.AddOptions();
            var container = new ContainerBuilder();
            container.Populate(services);
            return new AutofacServiceProvider(container.Build());
            #endregion
        }


        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
        {
            //容器根路径
            var pathBase = Configuration["PATH_BASE"];
            if (!string.IsNullOrEmpty(pathBase))
                app.UsePathBase(pathBase);

            app.UseSwagger()
           .UseSwaggerUI(setup =>
           {
               setup.SwaggerEndpoint($"{ (!string.IsNullOrEmpty(pathBase) ? pathBase : string.Empty) }/swagger/v1/swagger.json", "Product.API V1");
               setup.OAuthClientId("productswaggerui");
               setup.OAuthAppName("商品服务授权");
           });

            app.UseRouting();
            app.UseCors("CorsPolicy");
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseSession();
            app.UseStaticFiles();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<ProductServer>();
                endpoints.MapDefaultControllerRoute();
                endpoints.MapControllers();
                endpoints.MapGet("/_proto/", async ctx =>
                {
                    ctx.Response.ContentType = "text/plain";
                    using var fs = new FileStream(Path.Combine(env.ContentRootPath, "Protos", "product.proto"), FileMode.Open, FileAccess.Read);
                    using var sr = new StreamReader(fs);
                    while (!sr.EndOfStream)
                    {
                        var line = await sr.ReadLineAsync();
                        if (line != "/* >>" || line != "<< */")
                        {
                            await ctx.Response.WriteAsync(line);
                        }
                    }
                });
                endpoints.MapHealthChecks("/hc", new HealthCheckOptions()
                {
                    Predicate = _ => true,
                    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
                });
                endpoints.MapHealthChecks("/liveness", new HealthCheckOptions
                {
                    Predicate = r => r.Name.Contains("self")
                });
            });



            ConfigureEventBus(app);
        }

        private void ConfigureEventBus(IApplicationBuilder app)
        {
            //var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();

            //eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>();
            //eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>();
        }

    }
}
